// $Id: DataTransferObject.cs 10 2008-02-29 15:58:02Z mcartoixa $
/*
 *  Copyright (C) 2007 NourY Solutions
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 3 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 */
/*! \file
 * \brief Interfaces of Data Transfer Objects.
 *
 * \author $Author: mcartoixa $
 * \date $Date: 2008-02-29 16:58:02 +0100 (ven., 29 févr. 2008) $
 * \version $Revision: 10 $
 */

using System;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
#if (!NET_CF)
using System.Runtime.Serialization;
#endif

namespace Salamanca.DataAccess
{



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Interface implemented by a <include file="../doc/Global.doc.xml" path="doc/template[@name='DataTransferObject']/*" />.</summary>
    /// <example>
    /// <code>
    /// public partial class PersonDataTransfer:
    ///     IDataTransferObject
    /// {
    ///     public object Clone()
    ///     {
    ///         return MemberwiseClone();
    ///     }
    ///
    ///     public int Id;
    ///     public string Name;
    /// }
    /// </code>
    /// </example>
    ///
    ///////////////////////////////////////////////////////////////////////////

//    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1040:AvoidEmptyInterfaces")]
    public interface IDataTransferObject:
        ICloneable
    {
    }



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Interface implemented by a transferable type.</summary>
    /// <remarks>
    ///   <para>A transferable type gives access to a <include file="../doc/Global.doc.xml" path="doc/template[@name='DataTransferObject']/*" />.</para>
    /// </remarks>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public interface ITransferable
    {

        /// <summary>Gets the <include file="../doc/Global.doc.xml" path="doc/template[@name='DataTransferObject']/*" />
        /// filled with the current instance data.</summary>
        /// <summary>The <include file="../doc/Global.doc.xml" path="doc/template[@name='DataTransferObject']/*" />
        /// filled with the current instance data.</summary>
        [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        IDataTransferObject GetData();

        /// <summary>Assigns the specified data to the current instance.</summary>
        /// <param name="data">The <include file="../doc/Global.doc.xml" path="doc/template[@name='DataTransferObject']/*" /> containing the data.</param>
        [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        void SetData(IDataTransferObject data);

    }



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Interface implemented by a <include file="../doc/Global.doc.xml" path="doc/template[@name='DataTransferObject']/*" />
    /// for a read-only <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" />.</summary>
    /// <remarks>
    ///   <para>In addition to its persistent data, a <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" />
    /// must be able to transfer its internal state data.</para>
    /// </remarks>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public interface IDomainEntityDataTransferObject:
        IDataTransferObject
    {

        /// <summary>A <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> persistent data.</summary>
        IDataTransferObject Data
        {
            get;
            set;
        }

        /// <summary>Unused.</summary>
        IDictionary Childs
        {
            get;
            set;
        }

        /// <summary>A <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> internal state data.</summary>
        bool IsLoaded
        {
            get;
            set;
        }

        /// <summary>A <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> internal state data.</summary>
        bool IsModified
        {
            get;
            set;
        }

        /// <summary>A <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> internal state data.</summary>
        bool IsNew
        {
            get;
            set;
        }
    }



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Implementation of a <include file="../doc/Global.doc.xml" path="doc/template[@name='DataTransferObject']/*" />
    /// for a <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" />.</summary>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public class DomainEntityDataTransferObject<TDataTransferObject>:
        IDomainEntityDataTransferObject
        where TDataTransferObject:
            class, // SOAP serialization does not accept structs...
            IDataTransferObject,
            new()
    {

        /// <summary>Constructor.</summary>
        public DomainEntityDataTransferObject()
        {
        }

        /// <summary>Constructor.</summary>
        public DomainEntityDataTransferObject(TDataTransferObject data, IDictionary childs, bool isLoaded, bool isModified, bool isNew)
        {
            _Data=data;
            _ChildsData=new DictionarySerializer(childs);
            _IsLoaded=isLoaded;
            _IsModified=isModified;
            _IsNew=isNew;
        }

        /// <summary>Creates a new object that is a copy of the current instance.</summary>
        public virtual DomainEntityDataTransferObject<TDataTransferObject> Clone()
        {
            return new DomainEntityDataTransferObject<TDataTransferObject>((TDataTransferObject)Data.Clone(), Childs, IsLoaded, _IsModified, _IsNew);
        }

        /// <summary>Creates a new object that is a copy of the current instance.</summary>
        object ICloneable.Clone()
        {
            return Clone();
        }

        /// <summary>A <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> persistent data.</summary>
        public TDataTransferObject Data
        {
            get
            {
                return _Data;
            }
            set
            {
                _Data=value;
            }
        }

        /// <summary>Unused.</summary>
        [XmlIgnore]
        [SoapIgnore]
        public IDictionary Childs
        {
            get
            {
                return _ChildsData.Dictionary;
            }
            set
            {
                _ChildsData.Dictionary=value;
            }
        }

        /// <summary>Unused.</summary>
        [XmlIgnore] // We may care about this later...
        [SoapIgnore]
        internal DictionarySerializer ChildsData
        {
            get
            {
                return _ChildsData;
            }
            set
            {
                _ChildsData=value;
            }
        }

        /// <summary>A <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> internal state data.</summary>
        public bool IsLoaded
        {
            get
            {
                return _IsLoaded;
            }
            set
            {
                _IsLoaded=value;
            }
        }

        /// <summary>A <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> internal state data.</summary>
        public bool IsModified
        {
            get
            {
                return _IsModified;
            }
            set
            {
                _IsModified=value;
            }
        }

        /// <summary>A <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> internal state data.</summary>
        public bool IsNew
        {
            get
            {
                return _IsNew;
            }
            set
            {
                _IsNew=value;
            }
        }

        /// <summary>A <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainEntity']/*" /> persistent data.</summary>
        IDataTransferObject IDomainEntityDataTransferObject.Data
        {
            get
            {
                return Data;
            }
            set
            {
                Data=(TDataTransferObject)value;
            }
        }


        [NonSerialized]
        private DictionarySerializer _ChildsData;
        private TDataTransferObject _Data;
        private bool _IsLoaded=true;
        private bool _IsModified;
        private bool _IsNew;

    }

    /// <summary>Class used to serialize a <see cref="IDictionary" /> instance in XML.</summary>
    /// <remarks>This code has been inspired by <a href="http://msdn.microsoft.com/msdnmag/issues/03/06/XMLFiles/">MSDN Magazine, June 2003</a>.</remarks>
    [XmlSchemaProvider("GetSchema")]
    [SoapType("Dictionary", "http://www.codeplex.com/salamanca/dataaccess")]
    [XmlRoot("Dictionary", Namespace="http://www.codeplex.com/salamanca/dataaccess")]
    internal class DictionarySerializer:
        IXmlSerializable
    {
        static DictionarySerializer()
        {
            _XmlSerializerNamespaces=new XmlSerializerNamespaces();
            _XmlSerializerNamespaces.Add("nyda", NS);
        }

        /// <summary>Initializes a new instance of the <see cref="DictionarySerializer" /> class.</summary>
        public DictionarySerializer()
        {
        }

        /// <summary>Initializes a new instance of the <see cref="DictionarySerializer" /> class.</summary>
        public DictionarySerializer(IDictionary dictionary)
        {
            Dictionary=dictionary;
        }

        /// <summary>Gets the schema used for the serialization of the <see cref="IDictionary" /> instance.</summary>
        public static XmlSchemaComplexType GetSchema(XmlSchemaSet xs)
        {
            XmlSchema schema=XmlSchema.Read(Assembly.GetExecutingAssembly().GetManifestResourceStream("Salamanca.DataAccess.Resources.DictionarySchema.xsd"), null);
            xs.Add(schema);

            return (XmlSchemaComplexType)schema.SchemaTypes[new XmlQualifiedName("DictionaryType", NS)];
        }

        /// <summary>Converts the <see cref="IDictionary" /> instance into its XML representation.</summary>
        void IXmlSerializable.WriteXml(XmlWriter w)
        {
            if (Dictionary!=null)
            {
                w.WriteStartElement("dictionary", NS);
                foreach (object key in Dictionary.Keys)
                {
                    object value=Dictionary[key];
                    w.WriteStartElement("item", NS);

                    w.WriteStartElement("key", NS);
                    w.WriteAttributeString("type", NS, key.GetType().FullName);
                    XmlSerializer keySerializer=new XmlSerializer(key.GetType(), NS);
                    keySerializer.Serialize(w, key, _XmlSerializerNamespaces);
                    w.WriteEndElement();

                    w.WriteStartElement("value", NS);
                    w.WriteAttributeString("type", NS, value.GetType().FullName);
                    XmlSerializer valueSerializer=new XmlSerializer(value.GetType(), NS);
                    valueSerializer.Serialize(w, value, _XmlSerializerNamespaces);
                    w.WriteEndElement();

                    w.WriteEndElement();
                }
                w.WriteEndElement();
            }
        }

        /// <summary>Generates a <see cref="IDictionary" /> instance from its XML representation.</summary>
        void IXmlSerializable.ReadXml(XmlReader r)
        {
            r.Read(); // move past container
            try
            {
                r.ReadStartElement("dictionary");
                Dictionary=new HybridDictionary(0);

                while (r.NodeType!=XmlNodeType.EndElement)
                {
                    r.ReadStartElement("item", NS);

                    r.ReadStartElement("key", NS);
                    r.MoveToAttribute("type", NS);
                    Type keyType=Type.GetType(r.ReadContentAsString());
                    XmlSerializer keySerializer=new XmlSerializer(keyType, NS);
                    r.MoveToContent();
                    object key=keySerializer.Deserialize(r);
                    r.ReadEndElement();
                    r.MoveToContent();

                    r.ReadStartElement("value", NS);
                    r.MoveToAttribute("type", NS);
                    Type valueType=Type.GetType(r.ReadContentAsString());
                    XmlSerializer valueSerializer=new XmlSerializer(valueType, NS);
                    r.MoveToContent();
                    object value=valueSerializer.Deserialize(r);
                    r.ReadEndElement();
                    r.MoveToContent();

                    Dictionary.Add(key, value);
                }
            } catch (XmlException)
            {
            }
        }

        /// <summary>This method is not implemented.</summary>
        /// <exception cref="NotImplementedException">Always.</exception>
        XmlSchema IXmlSerializable.GetSchema()
        {
            throw new NotImplementedException();
        }

        private const string NS="http://www.codeplex.com/salamanca/dataaccess";
        private static XmlSerializerNamespaces _XmlSerializerNamespaces;

        /// <summary>The instance to be serialized.</summary>
        public IDictionary Dictionary;

    }

}
